﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace DDD.Core {
    /// <summary>
    /// バーテックス シェーダー クラス
    /// </summary>
    /// <remarks>
    /// <img src="NotImplemented">シェーダーのブロック図</img>
    /// バーテックス シェーダーは
    /// <see cref="ShaderProgram"/> を構成する2つのシェーダーのうちの1つです｡
    /// バーテックス シェーダーは1頂点につき1回実行されます｡
    /// シェーダーはソース コードの状態でファイル､もしくはメモリからロードされ､コンパイルされると
    /// <see cref="ShaderProgram"/> によってリンク可能になります｡
    /// <h3>コンパイルのタイミング</h3>
    /// <para>
    /// OpenGLの場合一般的にはコンパイルはドライバーが行い､呼び出すにはOpenGLコンテキストが必要です｡
    /// DDDはレンダリングコンテキスト依存部分は拡張メソッドで実装されているので､
    /// このクラスのインスタンス メソッドとしては <c>Compile</c> は存在しません｡
    /// コンパイルの詳細については､例えば <see cref="DDD.OpenTK.VerttexShaderExtensions"/> を参照してください｡
    /// 通常は必要に応じてDDDが自動でコンパイルするのでユーザーがタイミングを気にする必要はありません｡
    /// </para>
    /// </remarks>
    /// <seealso cref="ShaderProgram"/>
    /// <seealso cref="FragmentShader"/>
    /// <seealso cref="DDD.OpenTK.VertexShaderExtensions"/>

    public class VertexShader : Object {

        #region Field
        string source;
        string log;
        #endregion

        #region Property
        /// <summary>
        /// シェーダー ソース文字列
        /// </summary>
        /// <remarks>
        /// このシェーダーのソース コードです｡
        /// コンストラクターの中で設定され､文字列はその後ずっと保存されます｡
        /// </remarks>
        public string Source {
            get { return source; }
        }

        /// <summary>
        /// コンパイル ログ
        /// </summary>
        /// <remarks>
        /// このシェーダーをコンパイルした時のコンパイル ログです｡コンパイル前は空の文字列が返ります｡
        /// 返される文字列はドライバーに依存し､かつ予告無く変更される場合があります｡
        /// コンパイルが成功したか失敗したかを判定する目的で､このログを使用しないでください｡
        /// このログはデバッグを用途を想定しています｡
        /// </remarks> 
        public string CompileLog {
            get { return log; }
        }

        /// <inheritdoc/>
        public override IEnumerable<Object> References {
            get {
                return base.References;
            }
        }
        #endregion

        #region Constructor
        /// <summary>
        /// ファイルから読み込んで <see cref="VertexShader"/> オブジェクトを作成するコンストラクター
        /// </summary>
        /// <remarks>
        /// ファイルから読み込んで <see cref="VertexShader"/> オブジェクトを作成します｡
        /// この段階ではソースはまだコンパイルされません。
        /// 従ってコードの妥当性も未検証です｡
        /// コンパイルのタイミングについては <see cref="VertexShader"/> の解説を参照してください。
        /// </remarks>
        /// <param name="name">ファイル名</param>
        /// <exception cref="ArgumentNullException"><paramref name="name"/> が <c>null</c>､または空文字</exception>
        /// <exception cref="FileNotFoundException">ファイルのオープンに失敗した</exception>
        public VertexShader (string name) {
            if (name == null || name == "") {
                throw new ArgumentNullException ("Shader name is null or empty");
            }
            using (var reader = new StreamReader (name)) {
                this.source = reader.ReadToEnd ();
            }
            this.log = "";
        }

        /// <summary>
        /// メモリから読み込んで <see cref="VertexShader"/> オブジェクトを作成するコンストラクター
        /// </summary>
        /// <remarks>
        /// メモリから読み込んで <see cref="VertexShader"/> オブジェクトを作成します｡
        /// この段階ではソースはまだコンパイルされません。
        /// 従ってコードの妥当性も未検証です｡
        /// コンパイルのタイミングについては <see cref="VertexShader"/> の解説を参照してください。
        /// </remarks>
        /// <param name="stream">メモリー ストリーム</param>
        /// <exception cref="ArgumentNullException"><paramref name="stream"/> が <c>null</c></exception>
        public VertexShader (MemoryStream stream) {
            if (stream == null) {
                throw new ArgumentNullException ("Stream is null");
            }
            
            this.source = new StreamReader (stream).ReadToEnd ();
            this.log = "";
    }
        #endregion

        #region Method

        /// <summary>
        /// ログの変更
        /// </summary>
        /// <remarks>
        /// このメソッドは拡張メソッドがログをセットするのに使用します｡
        /// </remarks>
        /// <param name="log">ログ</param>
        internal void SetLog (string log) {
            this.log = log;
        }
        #endregion


    }
}
